package com.ylr.user.framework.service.impl;

import com.ylr.user.framework.entity.UserEntity;
import com.ylr.user.framework.mapper.UserMapper;
import com.ylr.user.framework.service.HistoryPasswordService;
import com.ylr.user.framework.service.UserContactFlowService;
import com.ylr.user.framework.service.UserService;
import com.ylr.base.common.utils.*;
import com.ylr.admin.framework.resolver.YaUserResolver;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ylr.base.common.constant.RedisConstant;
import com.ylr.base.common.dto.NameValue;
import com.ylr.base.common.dto.PageDto;
import com.ylr.base.common.enums.CommonEnum;
import com.ylr.base.common.enums.ModuleEnum;
import com.ylr.base.common.enums.ResultEnum;
import com.ylr.base.common.exception.YlrException;
import com.ylr.base.common.po.user.LoginUser;
import com.ylr.base.common.po.user.UserAuthenticationDto;
import com.ylr.base.common.po.user.UserPo;
import com.ylr.base.common.tool.PageQuery;
import com.ylr.base.framework.service.AsyncService;
import com.ylr.base.framework.service.SnowflakeService;
import com.ylr.basic.framework.resolver.YbAesResolver;
import com.ylr.config.common.enums.KeyEnum;
import com.ylr.config.framework.resolver.YcMappingResolver;
import com.ylr.redis.framework.service.RedisHashService;
import com.ylr.user.common.dto.user.UserDto;
import com.ylr.user.common.enums.UserContactFlowEnum;
import com.ylr.user.common.enums.UserEnum;
import com.ylr.user.common.vo.user.UserFormVo;
import com.ylr.user.common.vo.user.UserVo;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.PathMatcher;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wyw
 * date: 2023-04-21 11:44:08
 * className: UserServiceImpl
 * version: 1.0
 * description:
 */
@Service("userService")
@AllArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> implements UserService {

    private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);

    private final HistoryPasswordService historyPasswordService;

    private final UserContactFlowService userContactFlowService;

    private final YaUserResolver yaUserResolver;

    private final YbAesResolver ybAesResolver;

    private final YcMappingResolver ycMappingResolver;

    private final SnowflakeService snowflakeService;

    private final ThrottleUtils throttleUtils;

    private final RedisHashService redisHashService;

    private final AsyncService asyncService;

    @Override
    public UserPo auth(String userId, String module, String channel, String uri) {
        // 获取所属模块值
        Integer moduleValue = Optional.ofNullable(ModuleEnum.getModuleEnum(module)).map(ModuleEnum::getValue).orElse(-1);

        // 1、获取登录用户对象
        LoginUser loginUser = redisHashService.get(RedisConstant.SECURITY_USER_MAP, userId);
        UserAuthenticationDto userAuthenticationDto;
        UserPo userPo;
        if (Objects.nonNull(loginUser)) {
            userAuthenticationDto = loginUser.getAuthenticationDto();
            userPo = loginUser.getUserPo();
        } else {
            long _userId = Long.parseLong(userId);

            userAuthenticationDto = yaUserResolver.getAuthentication(_userId);
            userPo = this.getUserPo(_userId);
        }

        Map<Integer, Set<String>> moduleUriSetMap = userAuthenticationDto.getModuleUriSetMap();
        Set<String> uriSet = moduleUriSetMap.getOrDefault(moduleValue, Collections.emptySet());

        // 2、用户认证
        boolean pass = uriSet.contains(uri);
        if (!pass) {
            /*
                直接路由匹配不到，往下接着采用模糊匹配
             */
            PathMatcher pathMatcher = new AntPathMatcher();

            Map<Integer, List<String>> moduleUriListMap = userAuthenticationDto.getModuleUriListMap();
            List<String> uriList = moduleUriListMap.getOrDefault(moduleValue, Collections.emptyList());
            for (String _uri : uriList) {
                if (pathMatcher.match(_uri, uri)) {
                    // 匹配成功
                    pass = true;
                    break;
                }
            }
            if (!pass) {
                // 直接抛出异常，返回错误结果，表示认证失败
                throw new YlrException(ResultEnum.AUTH_FAIL);
            }
        }

        // 3、用户状态判断
        UserEnum statusEnum = UserEnum.getStatusEnum(userPo.getStatus());
        if (statusEnum == UserEnum.RISK_STATUS) {
            // 直接抛出异常，返回错误结果，表示认证失败
            throw new YlrException(ResultEnum.RISK_USER);
        } else if (statusEnum == UserEnum.DISABLE_STATUS) {
            // 直接抛出异常，返回错误结果，表示认证失败
            throw new YlrException(ResultEnum.DISABLE_USER);
        }

        // 4、添加用户缓存
        Runnable runnable = () -> {
            // 获取用户登录数据

            LoginUser _loginUser = new LoginUser();

            _loginUser.setUserPo(userPo)
                    .setAuthenticationDto(userAuthenticationDto);

            // 缓存登录用户
            redisHashService.put(RedisConstant.SECURITY_USER_MAP, userId, _loginUser);
        };
        throttleUtils.throttleRun(runnable, "auth-" + userId, 1, TimeUnit.SECONDS);

        return userPo;
    }

    @Override
    public Long saveUser(UserVo userVo) {
        // 获取正在添加用户的用户对象
        UserPo userPo = UserUtils.getUser();
        Date date = new Date();

        UserEntity user = this.newUser(userVo, userPo, date);
        // 1、插入用户
        baseMapper.insert(user);
        log.info("保存用户完成");

        asyncService.runAsync(() -> {
            // 2、保存用户密码
            historyPasswordService.saveUserPassword(user.getId(), user.getPassword());
            if (StringUtils.isNotBlank(user.getMobile())) {
                // 3、保存用户手机号记录
                userContactFlowService.saveUserContactFlow(user.getId(), UserContactFlowEnum.MOBILE.getValue(),
                        UserContactFlowEnum.BIND.getValue(), user.getMobile(), Boolean.FALSE);
            }
            if (StringUtils.isNotBlank(user.getEmail())) {
                // 4、保存用户邮箱号记录
                userContactFlowService.saveUserContactFlow(user.getId(), UserContactFlowEnum.EMAIL.getValue(),
                        UserContactFlowEnum.BIND.getValue(), user.getEmail(), Boolean.FALSE);
            }
        });

        return user.getId();
    }

    @Override
    public PageDto<UserDto> pageUserList(UserFormVo userFormVo) {

        // 1、查询数据
        // mp分页对象
        Page<UserEntity> page = PageQuery.initPage(userFormVo);

        LambdaQueryWrapper<UserEntity> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
        // 查询字段
        userLambdaQueryWrapper.select(UserEntity::getId, UserEntity::getDeptId, UserEntity::getUsername, UserEntity::getSubMobile,
                UserEntity::getSubEmail, UserEntity::getLoginName, UserEntity::getStatus, UserEntity::getLeaderStatus,
                UserEntity::getUpdateTime, UserEntity::getUpdateUser, UserEntity::getCreateTime, UserEntity::getCreateUser);
        // 匹配条件
        userLambdaQueryWrapper.eq(Objects.nonNull(userFormVo.getDeptId()), UserEntity::getDeptId, userFormVo.getDeptId())
                .eq(Objects.nonNull(userFormVo.getStatus()), UserEntity::getStatus, userFormVo.getStatus())
                .like(StringUtils.isNotBlank(userFormVo.getWord()), UserEntity::getUsername, userFormVo.getWord())
                .eq(UserEntity::getDeleteStatus, CommonEnum.DELETE_NO.getValue());
        // 分页查询
        baseMapper.selectPage(page, userLambdaQueryWrapper);

        List<UserEntity> userList = page.getRecords();
        long totalNum = page.getTotal();

        // 2、数据封装
        List<UserDto> userDtoList = new ArrayList<>(userList.size());
        for (UserEntity user : userList) {
            userDtoList.add(this.getUserDto(user));
        }

        return new PageDto<>(userDtoList, totalNum, userFormVo.getPageNo(), userFormVo.getPageSize());
    }

    @Override
    public UserDto getUser(Long id) {
        // 查询用户
        UserEntity user = this.findUser(id);

        return this.getUserDto(user);
    }

    @Override
    public List<UserDto> getUserList(List<Long> userIdList) {
        // 查询用户
        List<UserEntity> userList = baseMapper.selectBatchIds(userIdList);
        if (userList.isEmpty()) {
            log.error("查询用户不存在");
            throw new YlrException(ResultEnum.NOT_FOUND);
        }
        // 数据构造
        List<UserDto> userDtoList = userList.stream()
                .filter(user -> CommonEnum.DELETE_NO.getValue().equals(user.getDeleteStatus()))
                .map(this::getUserDto)
                .collect(Collectors.toList());
        if (userDtoList.isEmpty()) {
            log.info("查询用户已被删除");
            throw new YlrException(ResultEnum.NOT_FOUND);
        }
        return userDtoList;
    }

    @Override
    public Map<Long, String> getUsernameMap(Set<Long> userIdSet) {
        if (CollectionUtils.isEmpty(userIdSet)) {
            return Collections.emptyMap();
        }
        if (userIdSet.size() == 1) {
            // 一个主键
            UserEntity user = findUser(userIdSet.iterator().next());
            return Collections.singletonMap(user.getId(), user.getUsername());
        }
        LambdaQueryWrapper<UserEntity> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
        // 查询字段
        userLambdaQueryWrapper.select(UserEntity::getId, UserEntity::getUsername);
        // 匹配条件
        userLambdaQueryWrapper.in(UserEntity::getId, userIdSet)
                .eq(UserEntity::getDeleteStatus, CommonEnum.DELETE_NO.getValue());
        // 查询数据
        List<UserEntity> userList = baseMapper.selectList(userLambdaQueryWrapper);
        if (userList.isEmpty()) {
            log.info("用户列表为空");
            return Collections.emptyMap();
        }
        // 数据构造
        return userList.stream().collect(Collectors.toMap(UserEntity::getId, UserEntity::getUsername));
    }

    @Override
    public List<NameValue<String>> getUsernameList(Set<Long> userIdSet) {
        if (CollectionUtils.isEmpty(userIdSet)) {
            return Collections.emptyList();
        }
        if (userIdSet.size() == 1) {
            // 一个主键
            UserEntity user = findUser(userIdSet.iterator().next());
            return Collections.singletonList(new NameValue<>(user.getUsername(), user.getId().toString()));
        }
        LambdaQueryWrapper<UserEntity> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
        // 查询字段
        userLambdaQueryWrapper.select(UserEntity::getId, UserEntity::getUsername);
        // 匹配条件
        userLambdaQueryWrapper.in(UserEntity::getId, userIdSet)
                .eq(UserEntity::getDeleteStatus, CommonEnum.DELETE_NO.getValue());
        // 查询数据
        List<UserEntity> userList = baseMapper.selectList(userLambdaQueryWrapper);
        if (userList.isEmpty()) {
            log.info("用户列表为空");
            return Collections.emptyList();
        }
        // 数据构造
        return userList.stream()
                .map(user -> new NameValue<>(user.getUsername(), user.getId().toString()))
                .collect(Collectors.toList());
    }

    @Override
    public void updateUser(UserVo userVo) {
        // 获取正在修改用户的用户对象
        UserPo userPo = UserUtils.getUser();
        Date date = new Date();

        UserEntity user = new UserEntity();
        user.setId(userVo.getId())
            .setDeptId(userVo.getDeptId())
            .setUsername(userVo.getUsername())
            .setUpdateTime(date)
            .setUpdateUser(userPo.getUsername())
            .setUpdateUserId(userPo.getId());
        // 更新用户
        int count = baseMapper.updateById(user);
        log.info("用户数据更新完成。count={}", count);
        if (count == 0) {
            throw new YlrException(ResultEnum.NOT_FOUND);
        }
    }

    @Override
    public void resetMobile() {
        // 获取正在重置手机号的用户
        UserPo userPo = UserUtils.getUser();

        UserEntity user = new UserEntity();
        user.setId(userPo.getId())
            .setMobile(StringUtils.EMPTY)
            .setSubMobile(StringUtils.EMPTY)
            .setUpdateTime(new Date())
            .setUpdateUser(userPo.getLoginName())
            .setUpdateUserId(userPo.getId());
        // 修改用户基础数据
        baseMapper.updateById(user);
        log.info("重置手机号完成");
    }

    @Override
    public void resetEmail() {
        // 获取正在重置邮箱号的用户
        UserPo userPo = UserUtils.getUser();

        UserEntity user = new UserEntity();
        user.setId(userPo.getId())
            .setEmail(StringUtils.EMPTY)
            .setSubEmail(StringUtils.EMPTY)
            .setUpdateTime(new Date())
            .setUpdateUser(userPo.getLoginName())
            .setUpdateUserId(userPo.getId());
        // 修改用户基础数据
        baseMapper.updateById(user);
        log.info("重置邮箱号完成");
    }

    @Override
    public Integer deleteUser(List<Long> idList) {
        // 校验删除数量是否超过最大阈值
        Integer batchMax = ycMappingResolver.getValue(KeyEnum.BATCH_OPT_NUM.getKey(), Integer.class);
        if (batchMax < idList.size()) {
            log.warn("一次删除接口数量超过阈值。batchMax={} idListSize={}", batchMax, idList.size());
            throw new YlrException(ResultEnum.BATCH_FAIL);
        }

        // 获取正在删除用户的用户对象
        UserPo userPo = UserUtils.getUser();

        LambdaUpdateWrapper<UserEntity> userLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        // 设置 字段值
        userLambdaUpdateWrapper.set(UserEntity::getDeleteStatus, CommonEnum.DELETE_YES.getValue())
                .set(UserEntity::getUpdateTime, new Date())
                .set(UserEntity::getUpdateUser, userPo.getUsername())
                .set(UserEntity::getUpdateUserId, userPo.getId());
        // 匹配条件
        userLambdaUpdateWrapper.in(UserEntity::getId, idList)
                .eq(UserEntity::getDeleteStatus, CommonEnum.DELETE_NO.getValue());
        // 修改数据
        int count = baseMapper.update(null, userLambdaUpdateWrapper);
        log.info("删除用户操作完成。count={}", count);
        if (count == 0) {
            throw new YlrException(ResultEnum.NOT_FOUND);
        }
        return count;
    }

    /**
     * 查找用户
     * @param id 用户主键
     * @return 用户实体对象
     */
    private UserEntity findUser(Long id) {
        // 查询用户
        UserEntity user = baseMapper.selectById(id);
        if (Objects.isNull(user)) {
            log.warn("用户不存在");
            throw new YlrException(ResultEnum.NOT_FOUND);
        }
        if (CommonEnum.DELETE_YES.getValue().equals(user.getDeleteStatus())) {
            log.warn("用户已被删除");
            throw new YlrException(ResultEnum.NOT_FOUND);
        }
        return user;
    }

    /**
     * 获取登录用户
     * @param userId 用户主键
     * @return 登录用户对象
     */
    private UserPo getUserPo(Long userId) {
        // 查询用户
        UserEntity user = this.findUser(userId);

        UserPo userPo = new UserPo();
        userPo.setId(user.getId())
            .setDeptId(user.getDeptId())
            .setUsername(user.getUsername())
            .setAvatar(user.getAvatar())
            .setMobile(Optional.of(user.getMobile()).filter(StringUtils::isNotEmpty).map(ybAesResolver::decodeHex).orElse(StringUtils.EMPTY))
            .setEmail(Optional.of(user.getEmail()).filter(StringUtils::isNotEmpty).map(ybAesResolver::decodeHex).orElse(StringUtils.EMPTY))
            .setLoginName(user.getLoginName())
            .setStatus(user.getStatus())
            .setLeaderStatus(user.getLeaderStatus());
        return userPo;
    }

    /**
     * 用户参数转化成用户实体对象
     * @param userVo 参数对象
     * @param userPo 操作用户对象
     * @param date   日期对象
     * @return 用户实体对象
     */
    private UserEntity newUser(UserVo userVo, UserPo userPo, Date date) {

        // 1、获取用户默认头像
        String avatar = ycMappingResolver.getStringValue(KeyEnum.DEFAULT_AVATAR.getKey());

        // 2、用户手机号和邮箱号加密
        String mobile = StringUtils.EMPTY;
        String subMobile = StringUtils.EMPTY;
        if (StringUtils.isNotBlank(userVo.getMobile())) {
            mobile = ybAesResolver.encryptToHex(userVo.getMobile());
            subMobile = DataUtils.dataMask(mobile);
        }
        String email = StringUtils.EMPTY;
        String subEmail = StringUtils.EMPTY;
        if (StringUtils.isNotBlank(userVo.getEmail())) {
            email = ybAesResolver.encryptToHex(userVo.getEmail());
            subMobile = DataUtils.dataMask(email);
        }
        /*
            3、密码加密
                3.1、解密前端传递密文密码（凯撒密文解密）
                3.2、加密密码（Sha256）
         */
        String password = CaesarUtils.decodePwd(userVo.getPassword());
        password = Sha256Utils.encodeSha256(password);

        UserEntity user = new UserEntity();
        user.setId(snowflakeService.getId())
            .setDeptId(userVo.getDeptId())
            .setUsername(userVo.getUsername())
            .setAvatar(avatar)
            .setMobile(mobile)
            .setSubMobile(subMobile)
            .setEmail(email)
            .setSubEmail(subEmail)
            .setLoginName(userVo.getLoginName())
            .setPassword(password)
            .setStatus(UserEnum.NORMAL_STATUS.getValue())
            .setLeaderStatus(userVo.getLeaderStatus())
            .setDeleteStatus(CommonEnum.DELETE_NO.getValue())
            .setUpdateTime(date)
            .setUpdateUser(StringUtils.EMPTY)
            .setUpdateUserId(CommonEnum.DEFAULT_ID.getLongValue())
            .setCreateTime(date)
            .setCreateUser(userPo.getUsername())
            .setCreateUserId(userPo.getId());
        return user;
    }

    /**
     * 用户实体对象封装
     * @param user 用户对象
     * @return 封装结果
     */
    private UserDto getUserDto(UserEntity user) {
        UserDto userDto = new UserDto();
        userDto.setId(user.getId().toString())
                .setDeptId(user.getDeptId().toString())
                .setUsername(user.getUsername())
                .setSubMobile(user.getSubMobile())
                .setSubEmail(user.getSubEmail())
                .setLoginName(user.getLoginName())
                .setStatus(user.getStatus())
                .setLeaderStatus(user.getLeaderStatus())
                .setUpdateTime(user.getUpdateTime())
                .setUpdateUser(user.getUpdateUser())
                .setCreateTime(user.getCreateTime())
                .setCreateUser(user.getCreateUser());
        return userDto;
    }
}